﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Objects.DataClasses;

namespace Dispatcher.Extensions
{
    /// <summary>
    /// Object set extensions.
    /// </summary>
    public static class EntityCollectionExtensions
    {
        /// <summary>
        /// Contains observable collections of object sets.
        /// </summary>
        private static readonly Dictionary<object, object> EntityCollectionItems = new Dictionary<object, object>();

        /// <summary>
        /// Contains observable collections of object sets.
        /// </summary>
        private static readonly Dictionary<object, object> EntityCollectionDeletedItems = new Dictionary<object, object>();

        /// <summary>
        /// Indicate observable collection refresh operation.
        /// </summary>
        private static bool _isRefresh;

        /// <summary>
        /// Refreshes the specified object set.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="entityCollection">The object set.</param>
        public static void Refresh<TEntity>(this EntityCollection<TEntity> entityCollection)
            where TEntity : EntityObject
        {
            if (!EntityCollectionItems.ContainsKey(entityCollection))
            {
                InitializeObservableCollections(entityCollection);
            }

            var items = (ObservableCollection<TEntity>)EntityCollectionItems[entityCollection];
            var deletedItems = (ObservableCollection<TEntity>)EntityCollectionDeletedItems[entityCollection];
            deletedItems.Clear();
            items.Clear();
            _isRefresh = true;
            items.AddRange(entityCollection);
            _isRefresh = false;
        }

        /// <summary>
        /// Adds the back deleted items.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="entityCollection">The object set.</param>
        public static void AddBackDeletedItems<TEntity>(this EntityCollection<TEntity> entityCollection) where TEntity : EntityObject
        {
            if (!EntityCollectionItems.ContainsKey(entityCollection))
            {
                InitializeObservableCollections(entityCollection);
            }

            var items = (ObservableCollection<TEntity>)EntityCollectionItems[entityCollection];
            var deletedItems = (ObservableCollection<TEntity>)EntityCollectionDeletedItems[entityCollection];
            items.AddRange(deletedItems);
            deletedItems.Clear();
        }

        /// <summary>
        /// Ases the observable collection.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="entityCollection">The object set.</param>
        /// <returns>Object set items as observable collection.</returns>
        public static ObservableCollection<TEntity> ItemsAsObservableCollection<TEntity>(this EntityCollection<TEntity> entityCollection)
            where TEntity : EntityObject
        {
            if (!EntityCollectionItems.ContainsKey(entityCollection))
            {
                InitializeObservableCollections(entityCollection);
            }

            return EntityCollectionItems[entityCollection] as ObservableCollection<TEntity>;
        }

        /// <summary>
        /// Deleteds the items as observable collection.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="entityCollection">The object set.</param>
        /// <returns>Object set deleted items as observable collection.</returns>
        public static ObservableCollection<TEntity> DeletedItemsAsObservableCollection<TEntity>(this EntityCollection<TEntity> entityCollection)
            where TEntity : EntityObject
        {
            if (!EntityCollectionDeletedItems.ContainsKey(entityCollection))
            {
                InitializeObservableCollections(entityCollection);
            }

            return EntityCollectionDeletedItems[entityCollection] as ObservableCollection<TEntity>;
        }

        /// <summary>
        /// Initializes the observable collections.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="entityCollection">The object set.</param>
        private static void InitializeObservableCollections<TEntity>(EntityCollection<TEntity> entityCollection)
            where TEntity : EntityObject
        {
            var itemsObservableCollection = new ObservableCollection<TEntity>(entityCollection);
            var deletedItemsObservableCollection = new ObservableCollection<TEntity>();
            EntityCollectionDeletedItems.Add(entityCollection, deletedItemsObservableCollection);
            EntityCollectionItems.Add(entityCollection, itemsObservableCollection);
            itemsObservableCollection.CollectionChanged +=
                (o, args) =>
                {
                    if (args.OldItems != null)
                    {
                        foreach (TEntity item in args.OldItems)
                        {
                            entityCollection.Remove(item);
                            deletedItemsObservableCollection.Add(item);
                        }
                    }

                    if (args.NewItems != null && !_isRefresh)
                    {
                        foreach (TEntity item in args.NewItems)
                        {
                            entityCollection.Add(item);
                        }
                    }
                };
        }
    }
}
